package hdgeom.primitives;

import java.awt.geom.Line2D;

import processing.core.PGraphics;
import processing.core.PVector;
import simpleGeom.Vec2D;

public abstract class AbstractLine3D extends AbstractShape {
	public abstract float x1();

	public abstract float y1();

	public abstract float z1();

	public abstract float x2();

	public abstract float y2();

	public abstract float z2();

	public float dX() {
		return x2() - x1();
	}

	public float dY() {
		return y2() - y1();
	}

	public float dZ() {
		return z2() - z1();
	}

	public float cX() {
		return (x1() + x2()) * 0.5f;
	}

	public float cY() {
		return (y1() + y2()) * 0.5f;
	}

	public float cZ() {
		return (z1() + z2()) * 0.5f;
	}

	public float[] getClosestPtToSegment(float x, float y, float z) {
		float vx = dX();
		float vy = dY();
		float vz = dZ();
		float c2 = dot(vx, vy, vz, vx, vy, vz);
		float dx = x - x1();
		float dy = y - y1();
		float dz = z - z1();
		float c1 = dot(dx, dy, dz, vx, vy, vz);
		if (c1 <= 0)
			return new float[] { x1(), y1(), z1() };
		if (c2 <= c1)
			return new float[] { x2(), y2(), z2() };
		float b = c1 / c2;
		float px = x1() + b * vx;
		float py = y1() + b * vy;
		float pz = z1() + b * vz;
		return new float[] { px, py, pz };

	}

	public float getLengthSqr() {
		return dX() * dX() + dY() * dY() + dZ() * dZ();
	}

	public float getLength() {
		return distance(x1(), y1(), z1(), x2(), y2(), z2());
	}

	public float dot(float x1, float y1, float z1, float x2, float y2, float z2) {
		return x1 * x2 + y1 * y2 + z1 * z2;
	}

	// dist_Point_to_Segment(): get the distance of a point to a segment
	// Input: a Point P and a Segment S (in any dimension)
	// Return: the shortest distance from P to S
	public float distToSegSqr(float x, float y, float z) {
		float vx = dX();
		float vy = dY();
		float vz = dZ();
		float c2 = dot(vx, vy, vz, vx, vy, vz);
		float dx = x - x1();
		float dy = y - y1();
		float dz = z - z1();
		float c1 = dot(dx, dy, dz, vx, vy, vz);
		if (c1 <= 0)
			return distanceSqr(x, y, z, x1(), y1(), z1());
		if (c2 <= c1)
			return distanceSqr(x, y, z, x2(), y2(), z2());
		float b = c1 / c2;
		float px = x1() + b * vx;
		float py = y1() + b * vy;
		float pz = z1() + b * vz;
		return distanceSqr(x, y, z, px, py, pz);
	}

	public float distToSeg(float x, float y, float z) {
		return (float) Math.sqrt(distToSegSqr(x, y, z));
	}

	public float distToLine(float x, float y, float z) {
		return (float) Math.sqrt(distToLineSqr(x, y, z));
	}

	public float distToLineSqr(float x, float y, float z) {
		float vx = dX();
		float vy = dY();
		float vz = dZ();
		float c2 = dot(vx, vy, vz, vx, vy, vz);
		float dx = x - x1();
		float dy = y - y1();
		float dz = z - z1();
		float c1 = dot(dx, dy, dz, vx, vy, vz);
		float b = c1 / c2;
		float px = x1() + b * vx;
		float py = y1() + b * vy;
		float pz = z1() + b * vz;
		return distanceSqr(x, y, z, px, py, pz);
	}

	public float distToSeg2D(float pX, float pY) {
		return distToSeg2D(x1(), y1(), x2(), y2(), pX, pY);
	}

	public float distToSeg2DSqr(float pX, float pY) {
		return distToSeg2DSqr(x1(), y1(), x2(), y2(), pX, pY);
	}

	public PVector segmentSegmentIntersection(AbstractLine3D l) {
		return segmentSegmentIntersection(x1(), y1(), x2(), y2(), l.x1(),
				l.y1(), l.x2(), l.y2());
	}

	public PVector segmentSegmentIntersection(double cX, double cY, double dX,
			double dY) {
		return segmentSegmentIntersection(x1(), y1(), x2(), y2(), cX, cY, dX,
				dY);
	}

	public PVector segmentRayIntersection(float cX, float cY, float dX, float dY) {
		return segmentRayIntersection(x1(), y1(), x2(), y2(), cX, cY, dX, dY);
	}

	public PVector segmentLineIntersection(float cX, float cY, float dX,
			float dY) {
		return segmentLineIntersection(x1(), y1(), x2(), y2(), cX, cY, dX, dY);
	}
	public PVector lineIntersection(float aX, float aY, float bX,
			float bY) {
		return lineLineIntersection(aX, aY, bX, bY, x1(), y1(), x2(), y2());
	}
	public PVector rayIntersection(float aX, float aY, float bX,
			float bY) {
		return rayLineIntersection(aX, aY, bX, bY, x1(), y1(), x2(), y2());
	}
	public PVector getBetweenRel(float f) {
		return new PVector(x1() + (x2() - x1()) * f, y1() + (y2() - y1()) * f,
				z1() + (z2() - z1()) * f);
	}

	public PVector getBetweenAbs(float f) {
		return getBetweenRel(f / getLength());
	}

	public void display2D(PGraphics g) {
		g.line(x1(), y1(), x2(), y2());
	}

	public void display3D(PGraphics g) {
		g.line(x1(), y1(), z1(), x2(), y2(), z2());
	}

	public PVector getCenter() {
		return new PVector((x1() + x2()) * 0.5f, (y1() + y2()) * 0.5f,
				(z1() + z2()) * 0.5f);
	}
	int relativeCCW(float x,float y){
		return Line2D.relativeCCW(x,y,x1(), y1(),x2(), y2());
	}
	@Override
	public int getNumVertices() {
		// TODO Auto-generated method stub
		return 2;
	}

	public Line getOffsetLine(float offset) {
		float vy = dX();
		float vx = -dY();
		float fac = offset / (float) Math.sqrt(vx * vx + vy * vy);
		vx *= fac;
		vy *= fac;
		return new Line(x1() + vx, y1() + vy, z1(), x2() + vx, y2() + vy,
				z2());
	}
	public Line getOffsetLine(float offset1,float offset2) {
		float vy = dX();
		float vx = -dY();
		float d=(float) Math.sqrt(vx * vx + vy * vy);
		float fac1 = offset1 / d;
		float fac2 = offset2 / d;
		float v1x =vx* fac1;
		float v1y =vy* fac1;
		float v2x =vx* fac2;
		float v2y =vy* fac2;
		return new Line(x1() + v1x, y1() + v1y, z1(), x2() + v2x, y2() + v2y,
				z2());
	}

	@Override
	public void getPosition(int i, float[] coords) {
		// TODO Auto-generated method stub
		if (i == 0) {
			coords[0] = x1();
			coords[1] = y1();
			coords[2] = y2();
		}
		if (i == 1) {
			coords[0] = x2();
			coords[1] = y2();
			coords[2] = y2();
		}
	}

}
